package org.finesys.common.netty.websocket.service.impl;

import io.netty.handler.codec.stomp.DefaultStompFrame;
import io.netty.handler.codec.stomp.StompCommand;
import io.netty.handler.codec.stomp.StompFrame;
import org.finesys.commom.jackson.util.JacksonUtil;
import org.finesys.common.netty.websocket.service.WebSocketSendService;
import org.finesys.common.netty.websocket.support.NettyChannelMap;
import org.finesys.common.netty.websocket.support.NettySession;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.UUID;

import static io.netty.handler.codec.stomp.StompHeaders.*;
import static org.finesys.common.netty.websocket.constants.WebSocketConstants.WEBSOCKET_MESSAGE_ALL;
import static org.finesys.common.netty.websocket.constants.WebSocketConstants.WEBSOCKET_MESSAGE_USER;

@Service
public class WebSocketStompSendServiceImpl implements WebSocketSendService {

    /**
     * 判断当前处理器是否支持指定的子协议。
     *
     * @param subProtocol 子协议名称
     * @return 如果当前处理器支持指定的子协议，则返回true；否则返回false
     */
    @Override
    public boolean supports(String subProtocol) {
        return "wss".equals(subProtocol);
    }

    /**
     * 通知所有用户success事件
     *
     * @param message
     */
    @Override
    public void convertAndSend(Object message) {
        convertAndSend(WEBSOCKET_MESSAGE_ALL, message);
    }

    /**
     * 发送事件到指定的目的地。
     *
     * @param destination 目标目的地的标识，如队列名、主题名等。
     * @param message     要发送的事件消息内容。
     */
    @Override
    public void convertAndSend(String destination, Object message) {
        //String messageId, String userName, String destination, Object message
        NettyChannelMap.getInstance().sendMessage(destination, convertToStompFrame("", "", destination, message));
    }

    /**
     * 向指定用户发送success事件通知。
     *
     * @param userName 用户名，用于指定接收事件通知的用户。
     * @param message  要发送的事件消息内容，可以是任何类型的对象。
     */
    @Override
    public void convertAndSendToUser(String userName, Object message) {
        convertAndSendToUser(userName, String.format(WEBSOCKET_MESSAGE_USER, userName), message);
    }

    /**
     * 向指定用户和目的地发送事件。
     *
     * @param userName    用户名，用于标识接收事件的特定用户。
     * @param destination 目标目的地的标识，可以是队列名、主题名或其他用于区分事件接收者的标识符。
     * @param message     要发送的事件消息内容。
     */
    @Override
    public void convertAndSendToUser(String userName, String destination, Object message) {
        NettySession nettySession = NettyChannelMap.getInstance().get(userName);
        if (nettySession != null) {
            nettySession.getChannel().writeAndFlush(convertToStompFrame("", userName, destination, message));
        }
    }

    /**
     * 从指定目的地移除发送任务。
     * <p>
     * 该方法用于取消或移除已经计划发送到指定目的地的发送任务。
     *
     * @param destination 目标目的地的标识，用于指定哪个目的地的发送任务需要被移除。
     */
    @Override
    public void removeSend(String destination) {
        NettyChannelMap.getInstance().remove(destination);

    }

    /**
     * 移除指定用户的发送任务。
     * <p>
     * 该方法用于取消或移除与指定用户名相关联的所有发送任务。
     *
     * @param userName 要移除发送任务的用户的用户名。
     */
    @Override
    public void removeSendUser(String userName) {
        NettyChannelMap.getInstance().removeUser(userName);
    }

    /**
     * 移除指定用户的指定目的地发送任务。
     * <p>
     * 该方法用于取消或移除与指定用户名相关联的、针对特定目的地的发送任务。
     *
     * @param userName    要移除发送任务的用户的用户名。
     * @param destination 目标目的地的标识，指定要移除的发送任务的目的地。
     */
    @Override
    public void removeSendUser(String destination, String userName) {
        NettyChannelMap.getInstance().remove(destination, userName);
    }


    /**
     * 将消息转换为STOMP帧
     *
     * @param messageId   消息ID
     * @param userName    用户名
     * @param destination 目标地址
     * @param message     消息内容
     * @return 转换后的STOMP帧
     */
    private static StompFrame convertToStompFrame(String messageId, String userName, String destination, Object message) {
        StompFrame stompFrame = new DefaultStompFrame(StompCommand.MESSAGE);
        stompFrame.headers().set(DESTINATION, destination);
        if (!StringUtils.hasText(messageId)) {
            messageId = UUID.randomUUID().toString();
        }
        stompFrame.headers().set(MESSAGE_ID, messageId);
        if (StringUtils.hasText(userName)) {
            stompFrame.headers().set(LOGIN, userName);
        }
        stompFrame.headers().set(MESSAGE, JacksonUtil.toJsonString(message));
        stompFrame.headers().set(CONTENT_TYPE, "application/json");
        return stompFrame;
    }
}
